#include "app_serial.h"
#include <stdlib.h>

#include <unistd.h>
#include "app_bluetooth.h"
#include "thirdparty/log/log.h"

static int app_serial_makeRaw(SerialDevice *serial_device)
{

    struct termios termios_p;
    // 实际设置串口的波特率
    // 获取串口的属性
    if (tcgetattr(serial_device->super.fd, &termios_p) < 0)
    {
        return -1;
    }
    // 改变属性
    // termios_p.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON);
    // termios_p.c_oflag &= ~OPOST;
    // termios_p.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
    // termios_p.c_cflag &= ~(CSIZE | PARENB);
    // termios_p.c_cflag |= CS8;
    cfmakeraw(&termios_p);
    // 将属性写回串口
    if (tcsetattr(serial_device->super.fd, TCSAFLUSH, &termios_p) < 0)
    {
        return -1;
    }
    return 0;
}

/**
 * @brief 设置串口是否处在阻塞模式
 *
 * @param serial_device 串口设备
 * @param enable 0:非阻塞模式，1:阻塞模式
 * @return int 0:成功，-1:失败
 */
static int app_serial_setBlockMode(SerialDevice *serial_device, int enable)
{
    struct termios termios_p;
    // 实际设置串口的波特率
    // 获取串口的属性
    if (tcgetattr(serial_device->super.fd, &termios_p) < 0)
    {
        return -1;
    }
    // 改变属性
    if (enable)
    {
        termios_p.c_cc[VMIN] = 1;
        termios_p.c_cc[VTIME] = 0;
    }
    else
    {
        termios_p.c_cc[VMIN] = 0;
        termios_p.c_cc[VTIME] = 5;
    }

    // 将属性写回串口
    if (tcsetattr(serial_device->super.fd, TCSAFLUSH, &termios_p) < 0)
    {
        return -1;
    }
    return 0;
}

SerialDevice *app_serial_init(char *filename)
{
    Device *device = app_device_init(filename);
    if (!device)
    {
        // 失败处理
        goto DEVICE_INIT_FAIL;
    }

    SerialDevice *serial_device = realloc(device, sizeof(SerialDevice));
    if (!serial_device)
    {
        // 失败处理
        goto SERIAL_DEVICE_INIT_FAIL;
    }

    if (app_serial_makeRaw(serial_device) < 0)
    {
        // 失败处理
        goto SERIAL_DEVICE_INIT_FAIL;
    }

    if (app_serial_setBaudRate(serial_device, BaudRate_115200) < 0)
    {
        // 失败处理
        goto SERIAL_DEVICE_INIT_FAIL;
    }
    // if (app_serial_setConnectionType(serial_device, CONN_BT) < 0)
    // {
    //     goto SERIAL_DEVICE_INIT_FAIL;
    // }
    if (app_serial_setParity(serial_device, Parity_None) < 0)
    {
        goto SERIAL_DEVICE_INIT_FAIL;
    }
    if (app_serial_setStopBits(serial_device, OneStopBit) < 0)
    {
        goto SERIAL_DEVICE_INIT_FAIL;
    }

    tcflush(serial_device->super.fd, TCIOFLUSH);
    log_info("serial_device init success");

    return serial_device;

SERIAL_DEVICE_INIT_FAIL:
    app_device_close(device);
DEVICE_INIT_FAIL:
    return NULL;
}

int app_serial_setBaudRate(SerialDevice *serial_device, SerialBaudRate baud_rate)
{
    struct termios termios_p;
    serial_device->baud_rate = baud_rate;
    // 实际设置串口的波特率
    // 获取串口的属性
    if (tcgetattr(serial_device->super.fd, &termios_p) < 0)
    {
        return -1;
    }
    // 改变属性
    cfsetispeed(&termios_p, baud_rate);
    cfsetospeed(&termios_p, baud_rate);

    // 将属性写回串口
    if (tcsetattr(serial_device->super.fd, TCSAFLUSH, &termios_p) < 0)
    {
        return -1;
    }
    return 0;
}

int app_serial_setParity(SerialDevice *serial_device, SerialParity parity)
{
    struct termios termios_p;
    serial_device->parity = parity;
    // 实际设置串口的波特率
    // 获取串口的属性
    if (tcgetattr(serial_device->super.fd, &termios_p) < 0)
    {
        return -1;
    }

    termios_p.c_cflag &= ~(PARENB | PARODD);
    termios_p.c_cflag |= parity;

    // 改变属性

    // 将属性写回串口
    if (tcsetattr(serial_device->super.fd, TCSAFLUSH, &termios_p) < 0)
    {
        return -1;
    }
    return 0;
}

int app_serial_setStopBits(SerialDevice *serial_device, SerialStopBits stop_bits)
{
    struct termios termios_p;
    serial_device->stop_bits = stop_bits;
    // 实际设置串口的波特率
    // 获取串口的属性
    if (tcgetattr(serial_device->super.fd, &termios_p) < 0)
    {
        return -1;
    }

    // 改变属性
    termios_p.c_cflag &= ~CSTOPB;
    termios_p.c_cflag |= stop_bits;

    // 将属性写回串口
    if (tcsetattr(serial_device->super.fd, TCSAFLUSH, &termios_p) < 0)
    {
        return -1;
    }
    return 0;
}

int app_serial_setConnectionType(SerialDevice *serial_device, ConnectionType connection_type)
{
    int ret;
    switch (connection_type)
    {
    case CONN_BT:
        serial_device->super.conn_type = CONN_BT;
        // 初始化蓝牙模块
        app_serial_setBaudRate(serial_device, BaudRate_9600);
        // 将串口设置为非阻塞模式
        app_serial_setBlockMode(serial_device, 0);
        tcflush(serial_device->super.fd, TCIOFLUSH);
        if (app_bluetooth_status(&serial_device->super) == 0)
        {
            // 尝试初始化蓝牙
            ret = app_bluetooth_rename(&serial_device->super, "XXYYZZ");
            if (ret < 0)
            {
                log_warn("app_bluetooth_rename failed");
            }

            ret = app_bluetooth_setNetID(&serial_device->super, "1111");
            if (ret < 0)
            {
                log_warn("app_bluetooth_setNetID failed");
            }
            ret = app_bluetooth_setMAddr(&serial_device->super, "0001");
            if (ret < 0)
            {
                log_warn("app_bluetooth_setMAddr failed");
            }
            ret = app_bluetooth_setBaudRate(&serial_device->super, BT_BAUD_115200);
            if (ret < 0)
            {
                log_warn("app_bluetooth_setBaudRate failed");
            }
            ret = app_bluetooth_reset(&serial_device->super);
            if (ret < 0)
            {
                log_warn("app_bluetooth_reset failed");
            }
        }
        sleep(1);
        app_serial_setBaudRate(serial_device, BaudRate_115200);
        tcflush(serial_device->super.fd, TCIOFLUSH);
        if (app_bluetooth_status(&serial_device->super) < 0) {
            log_error("bluetooth not ready");
            return -1;
        }
        serial_device->super.funcs->post_read = app_bluetooth_postRead;
        serial_device->super.funcs->pre_write = app_bluetooth_preWrite;
        // 设置完成后，将串口设置为阻塞模式
        app_serial_setBlockMode(serial_device, 1);
        tcflush(serial_device->super.fd, TCIOFLUSH);
        break;

    default:
        break;
    }
    return 0;
}
